package net.gdface.facelog;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map.Entry;

import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;

import gu.sql2java.IFuzzyMatchFilter.MatchErrorHandler;
import gu.sql2java.BaseBean;
import gu.sql2java.ColumnGetter;
import gu.sql2java.IStringMatchFilter;
import gu.sql2java.StringFieldSearcher;
import gu.sql2java.StringMatchType;
import gu.sql2java.TableManager;
import net.gdface.facelog.db.DeviceBean;
import net.gdface.facelog.db.DeviceGroupBean;
import net.gdface.facelog.db.IDeviceGroupManager;
import net.gdface.facelog.db.IPersonGroupManager;
import net.gdface.facelog.db.IDeviceManager;
import net.gdface.facelog.db.IPersonManager;
import net.gdface.facelog.db.PersonBean;
import net.gdface.facelog.db.PersonGroupBean;
import net.gdface.utils.BeanRelativeUtilits;
import net.gdface.utils.ConditionChecks;

import static net.gdface.facelog.db.Constant.FL_PERSON_ID_MOBILE_PHONE;
import static net.gdface.facelog.db.Constant.FL_PERSON_ID_PAPERS_NUM;

import static net.gdface.facelog.db.Constant.FL_DEVICE_ID_MAC;

import static gu.sql2java.Managers.instanceOf;

/**
 * 模糊查询
 * @author guyadong
 *
 */
class FuzzyMatchManagement implements MatchErrorHandler<String>,ServiceConstant{
	private final DaoManagement dm;
	/**
	 * 设备组模糊匹配对象
	 */
	private final GroupMatcher<DeviceGroupBean, IDeviceGroupManager> deviceGroupMatcher;
	/**
	 * 人员组模糊匹配对象
	 */
	private final GroupMatcher<PersonGroupBean, IPersonGroupManager> personGroupMatcher;
	/**
	 * 设备名字模糊匹配对象
	 */
	private final NameMatcher<DeviceBean, IDeviceManager> deviceNameMatcher;
	/**
	 * 设备MAC地址模糊匹配对象
	 */
	private final DeviceMacMatcher deviceMacMatcher;
	/**
	 * 人员名字模糊匹配对象
	 */
	private final NameMatcher<PersonBean, IPersonManager> personNameMatcher;
	/**
	 * 人员移动手机号模糊匹配对象
	 */
	private final PersonMobilePhoneMatcher mobilePhoneMatcher;
	/**
	 * 人员证件号码模糊匹配对象
	 */
	private final PersonPapersNumMatcher papersNumMatcher;
	FuzzyMatchManagement(DaoManagement dm) {
		this.dm = dm;
		this.deviceGroupMatcher = new GroupMatcher<>(IDeviceGroupManager.class);
		this.personGroupMatcher = new GroupMatcher<>(IPersonGroupManager.class);
		this.personNameMatcher = new NameMatcher<>(IPersonManager.class);
		this.deviceNameMatcher = new NameMatcher<>(IDeviceManager.class);
		
		this.mobilePhoneMatcher = new PersonMobilePhoneMatcher();
		this.papersNumMatcher = new PersonPapersNumMatcher();
		this.deviceMacMatcher = new DeviceMacMatcher();
	}
	
	FuzzyMatchManagement init(){
		logger.info("Device group fuzzy matcher initializing");
		deviceGroupMatcher.init();
		logger.info("Person group fuzzy matcher initializing");
		personGroupMatcher.init();
		logger.info("Device name fuzzy matcher initializing");
		deviceNameMatcher.init();
		logger.info("Device mac fuzzy matcher initializing");
		deviceMacMatcher.init();
		logger.info("Person name fuzzy matcher initializing");
		personNameMatcher.init();
		logger.info("Person mobile phone fuzzy matcher initializing");
		mobilePhoneMatcher.init();
		logger.info("Person papersnum matcher initializing");
		papersNumMatcher.init();

		return this;
	}
	/**
	 * 表字段模糊匹配查询<br>
	 * 根据指定的匹配条件({@code pattern})对{@code tablename}指定的表字段模糊匹配<br>
	 * 比如 '1102'匹配'1楼/1单元/102室'<br>
	 * {@code column} 取值与{@code tablename}的值相关<br>
	 * <pre>
	 * tablename值       -- column可取值
	 * fl_device            -- name,mac
	 * fl_person           -- name,papsers_num,mobile_phone
	 * fl_device_group -- 忽略
	 * fl_person_group-- 忽略
	 * </pre>
	 * {@code matchType}为{@code null}时,使用的默认匹配策略与{@code tablename}和{@code column}相关
	 * <pre>
	 * tablename,column值--默认匹配策略
	 * fl_device.name--支持通配符的字符串比较匹配{@link StringMatchType#WILDCARD_MATCH}
	 * fl_device.mac--支持通配符的字符串比较匹配{@link StringMatchType#WILDCARD_MATCH}
	 * fl_person.name--支持通配符的字符串比较匹配{@link StringMatchType#WILDCARD_MATCH}
	 * fl_person.papsers_num--支持通配符的字符串比较匹配{@link StringMatchType#WILDCARD_MATCH}
	 * fl_person.mobile_phone--数字模糊匹配{@link StringMatchType#DIGIT_FUZZY_MATCH}
	 * fl_device_group--右侧数字模糊匹配{@link StringMatchType#DIGIT_FUZZY_RIGHT_MATCH}
	 * fl_person_group--右侧数字模糊匹配{@link StringMatchType#DIGIT_FUZZY_RIGHT_MATCH}
	 * </pre>
	 * @param tablename 表名,必须为'fl_device_group'或'fl_person_group','fl_device','fl_person'
	 * @param column 指定模糊搜索的表字段名,可为{@code null}<br>
	 * @param pattern 模糊匹配条件,如'102'
	 * @param matchType 模糊匹配策略,为{@code null}使用默认值
	 * @param matchFlags 匹配标志
	 * @param pkFilter 主键过滤器,用于根据主键过滤表记录
	 * @param maxMatchCount 要求的最多匹配结果,如果返回的匹配结果超过此值则抛出异常,小于等于0时忽略
	 * @return 匹配结果列表,没有找到匹配记录时返回空表
	 * @throws FuzzyMatchCountExceedLimitException 返回的匹配结果超过{@code maxMatchCount}
	 */
	List<MatchEntry> fuzzySearch(String tablename,
			String column,
			String pattern, 
			StringMatchType matchType, 
			int matchFlags, 
			Predicate<Integer> pkFilter, int maxMatchCount) 
			throws FuzzyMatchCountExceedLimitException{
		List<MatchEntry> matched;
		if(deviceNameMatcher.getTablename().equals(tablename)){
			if(deviceNameMatcher.getEffectColumnName().equals(column)){
				matched = deviceNameMatcher.fuzzySearch(pattern, matchFlags, matchType, pkFilter);
			}else if(deviceMacMatcher.getEffectColumnName().equals(column)){
				matched = deviceMacMatcher.fuzzySearch(pattern, matchFlags, matchType, pkFilter);
			}else{
				throw new IllegalArgumentException("INVALID column " + column + " for table + " + tablename);
			}
		}else if(personNameMatcher.getTablename().equals(tablename)){
			if(personNameMatcher.getEffectColumnName().equals(column)){
				matched = personNameMatcher.fuzzySearch(pattern, matchFlags, matchType, pkFilter);
			}else if(mobilePhoneMatcher.getEffectColumnName().equals(column)){
				matched = mobilePhoneMatcher.fuzzySearch(pattern, matchFlags, matchType, pkFilter);
			}else if(papersNumMatcher.getEffectColumnName().equals(column)){
				matched = papersNumMatcher.fuzzySearch(pattern, matchFlags, matchType, pkFilter);
			}else{
				throw new IllegalArgumentException("INVALID column " + column  + " for table + " + tablename);
			}
		}else	if(deviceGroupMatcher.getTablename().equals(tablename)){
			matched = deviceGroupMatcher.fuzzySearch(pattern, matchFlags, matchType, pkFilter);
		}else if(personGroupMatcher.getTablename().equals(tablename)){
			matched = personGroupMatcher.fuzzySearch(pattern, matchFlags, matchType, pkFilter);
		}else{
			throw new IllegalArgumentException("INVALID tablename " + tablename);
		}
		ConditionChecks.checkTrue(maxMatchCount <= 0 || matched.size() <= maxMatchCount, 
				FuzzyMatchCountExceedLimitException.class,
				"too manay match count %s, exceed max limit %s",matched.size(),maxMatchCount);
		return matched;		
	}
	/**
	 * 表字段模糊匹配查询<br>
	 * 参见 {@link #fuzzySearch(String, String, String, StringMatchType, int, Predicate, int)}<br>
	 * {@code parentGroupId}和{@code recursive}参数用于创建主键过滤器
	 * @param tablename
	 * @param column
	 * @param pattern
	 * @param matchType
	 * @param matchFlags 匹配标志
	 * @param parentGroupId 父节点组ID(小于等于0时忽略),限定查询此节点之下的记录
	 * @param maxMatchCount
	 * @return 查询结果
	 * @throws FuzzyMatchCountExceedLimitException
	 * @see #fuzzySearch(String, String, String, StringMatchType, int, Predicate, int)
	 */
	List<MatchEntry> fuzzySearch(String tablename,
			String column,
			String pattern, 
			StringMatchType matchType, 
			int matchFlags, 
			int parentGroupId,int maxMatchCount) 
			throws FuzzyMatchCountExceedLimitException{
		Predicate<Integer> pkFilter = null;
		boolean recursive = (matchFlags & FM_RECURSIVE) != 0;
		if(parentGroupId > 0){
			if(deviceNameMatcher.getTablename().equals(tablename)){
				pkFilter = new DeviceGroupIdFilter(parentGroupId,recursive);
			}else if(personNameMatcher.getTablename().equals(tablename)){
				pkFilter = new PersonGroupIdFilter(parentGroupId,recursive);
			}else	if(deviceGroupMatcher.getTablename().equals(tablename)){
				pkFilter = new GroupParentFilter<DeviceGroupBean,IDeviceGroupManager>(IDeviceGroupManager.class,parentGroupId,recursive);
			}else if(personGroupMatcher.getTablename().equals(tablename)){
				pkFilter = new GroupParentFilter<PersonGroupBean,IPersonGroupManager>(IPersonGroupManager.class,parentGroupId,recursive);
			}else{
				throw new IllegalArgumentException("INVALID tablename " + tablename);
			}
		}
		return fuzzySearch(tablename, column, pattern, matchType, matchFlags, pkFilter, parentGroupId);
	}
	@Override
	public void onMatchError(Throwable e, String pattern) {
		logger.error("pattern={},{}:{}",pattern,e.getClass().getSimpleName(),e.getMessage());
	}
	
	protected static class StringFieldSearcherForMatchEntry<B extends BaseBean> extends StringFieldSearcher<B>{

		public <M extends TableManager<B>> StringFieldSearcherForMatchEntry(Class<M> interfaceClass,
				String... effectColumnNames) {
			super(interfaceClass, effectColumnNames);
		}
		public <M extends TableManager<B>> StringFieldSearcherForMatchEntry(Class<M> interfaceClass,
				int... effectColumnId) {
			super(interfaceClass, effectColumnId);
		}
		public final List<MatchEntry> fuzzySearch(String pattern, int matchFlags, IStringMatchFilter matchFilter, Predicate<Integer> pkFilter){
			Multimap<String, Integer> mm = super.search(pattern, matchFlags, matchFilter, pkFilter);
			List<MatchEntry> entrys = Lists.newArrayList();
			for(Entry<String, Integer> entry:mm.entries()){
				entrys.add(new MatchEntry(entry.getValue(), entry.getKey()));
			}
			// 按匹配的名字排序
			return BeanRelativeUtilits.sortByField(entrys, "key");
		}
		public final List<MatchEntry> fuzzySearch(String pattern, int matchFlags, StringMatchType matchType, Predicate<Integer> pkFilter){
			return fuzzySearch(pattern,matchFlags,matchType == null ? null : matchType.createMatchFilter(), pkFilter);
		}
	}
	/**
	 * 设备组/人员组路径模糊匹配对象<br>
	 * 默认使用'右侧数字模糊匹配'匹配策略
	 * @author guyadong
	 *
	 * @param <B> 表记录类型
	 * @param <M> 表访问实例类型
	 */
	private class GroupMatcher <B extends BaseBean,M extends TableManager<B>> extends StringFieldSearcherForMatchEntry<B>{

		public GroupMatcher(Class<M> interfaceClass) {
			super(interfaceClass, "name","parent");
			setDefaultMatchFilter(StringMatchType.DIGIT_FUZZY_RIGHT_MATCH);
			setFunKeyGetter(new GroupPathGetter<>(interfaceClass));
			setErrorHandler(FuzzyMatchManagement.this);
		}

	}
	/**
	 * 设备/人员名字{@code name}字段模糊匹配对象<br>
	 * 默认使用'支持通配符的字符串比较匹配'匹配策略
	 * @author guyadong
	 *
	 * @param <B> 表记录类型
	 * @param <M> 表访问实例类型
	 */
	private class NameMatcher <B extends BaseBean,M extends TableManager<B>> extends StringFieldSearcherForMatchEntry<B>{

		@SuppressWarnings("unchecked")
		public NameMatcher(Class<M> interfaceClass) {
			super(interfaceClass, "name");
			setDefaultMatchFilter(StringMatchType.WILDCARD_MATCH);
			setFunKeyGetter((Function<B, String>) new ColumnGetter<String>("name"));
			setErrorHandler(FuzzyMatchManagement.this);
		}

	}
	private class PersonMobilePhoneMatcher extends StringFieldSearcherForMatchEntry<PersonBean>{

		@SuppressWarnings("unchecked")
		public PersonMobilePhoneMatcher() {
			super(IPersonManager.class, FL_PERSON_ID_MOBILE_PHONE);
			setDefaultMatchFilter(StringMatchType.DIGIT_FUZZY_MATCH);
			Function<? extends BaseBean, String> f = new ColumnGetter<String>(FL_PERSON_ID_MOBILE_PHONE);
			setFunKeyGetter((Function<PersonBean, String>) f);
			setErrorHandler(FuzzyMatchManagement.this);
		}
	}
	private class PersonPapersNumMatcher extends StringFieldSearcherForMatchEntry<PersonBean>{

		@SuppressWarnings("unchecked")
		public PersonPapersNumMatcher() {
			super(IPersonManager.class, FL_PERSON_ID_PAPERS_NUM);
			setDefaultMatchFilter(StringMatchType.WILDCARD_MATCH);
			Function<? extends BaseBean, String> f = new ColumnGetter<String>(FL_PERSON_ID_PAPERS_NUM);
			setFunKeyGetter((Function<PersonBean, String>) f);
			setErrorHandler(FuzzyMatchManagement.this);
		}
	}
	private class DeviceMacMatcher extends StringFieldSearcherForMatchEntry<DeviceBean>{

		@SuppressWarnings("unchecked")
		public DeviceMacMatcher() {
			super(IDeviceManager.class, FL_DEVICE_ID_MAC);
			setDefaultMatchFilter(StringMatchType.WILDCARD_MATCH);
			Function<? extends BaseBean, String> f = new ColumnGetter<String>(FL_DEVICE_ID_MAC);
			setFunKeyGetter((Function<DeviceBean, String>) f);
			setErrorHandler(FuzzyMatchManagement.this);
		}
	}
	private class GroupPathGetter<B extends BaseBean,M extends TableManager<B>> implements Function<B, String>{
		
		private final Class<M> interfaceClass;
		public GroupPathGetter(Class<M> interfaceClass) {
			this.interfaceClass = interfaceClass;
		}

		@Override
		public String apply(B input) {
			return dm.daoGroupPathOf(interfaceClass,input);
		}
		
	}
	
	/**
	 * 基于人员组/设备组父节点限制的主键过滤器
	 * @author guyadong
	 *
	 * @param <B>
	 * @param <M>
	 */
	static class GroupParentFilter<B extends BaseBean,M extends TableManager<B>> implements Predicate<Integer>{
		private final int parent;
		private final boolean recursive;
		private final Method listOfParentMethod;
		private final M manager;
		private final Predicate<B> beanFilter;
		public GroupParentFilter(Class<M>interfaceClass,int parent, boolean recursive) {
			super();
			this.parent = parent;
			this.recursive = recursive;
			this.manager = instanceOf(interfaceClass);
			try {
				listOfParentMethod = interfaceClass.getMethod("listOfParent", Integer.class);
			} catch (Exception e) {
				Throwables.throwIfUnchecked(e);
				throw new RuntimeException(e);
			}
			this.beanFilter = new Predicate<B>(){

				@Override
				public boolean apply(B input) {
					return Objects.equal(input.getValue("parent"), GroupParentFilter.this.parent);
				}};
		}
		@SuppressWarnings("unchecked")
		@Override
		public boolean apply(Integer input) {
			if(input != null && parent > 0){
				if(recursive){
					try {
						List<B> parentList = (List<B>) listOfParentMethod.invoke(manager, input);
						return Iterables.tryFind(parentList, beanFilter).isPresent();
					} catch (InvocationTargetException e) {
						Throwables.throwIfUnchecked(e.getTargetException());
						throw new RuntimeException(e.getTargetException());
					} catch (Exception e) {
						Throwables.throwIfUnchecked(e);
						throw new RuntimeException(e);
					}
				}else{
					B bean = manager.loadByPrimaryKey(input);
					return Objects.equal(bean.getValue("parent"), parent);
				}
			}
			return false;
		}
	}
	/**
	 * 基于人员表的group_id限制的主键过滤器
	 * @author guyadong
	 *
	 */
	static class PersonGroupIdFilter extends GroupParentFilter<PersonGroupBean,IPersonGroupManager>{

		private final IPersonManager manager;
		public PersonGroupIdFilter(int parent, boolean recursive) {
			super(IPersonGroupManager.class, parent, recursive);
			manager = instanceOf(IPersonManager.class);
		}
		@Override
		public boolean apply(Integer input) {
			PersonBean bean = manager.loadByPrimaryKey(input);
			if(bean != null){
				super.apply(bean.getGroupId());
			}
			return false;
		}		
	}
	/**
	 * 基于设备表的group_id限制的主键过滤器
	 * @author guyadong
	 *
	 */
	static class DeviceGroupIdFilter extends GroupParentFilter<DeviceGroupBean,IDeviceGroupManager>{

		private final IDeviceManager manager;
		public DeviceGroupIdFilter(int parent, boolean recursive) {
			super(IDeviceGroupManager.class, parent, recursive);
			manager = instanceOf(IDeviceManager.class);
		}
		@Override
		public boolean apply(Integer input) {
			DeviceBean bean = manager.loadByPrimaryKey(input);
			if(bean != null){
				super.apply(bean.getGroupId());
			}
			return false;
		}		
	}
}
